{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://certificate.tpq.io/taim_logo.png\" width=\"350px\" align=\"right\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Artificial Intelligence in Finance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Risk Management"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dr Yves J Hilpisch | The AI Machine\n",
    "\n",
    "http://aimachine.io | http://twitter.com/dyjh"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from pylab import plt, mpl\n",
    "plt.style.use('seaborn')\n",
    "mpl.rcParams['savefig.dpi'] = 300\n",
    "mpl.rcParams['font.family'] = 'serif'\n",
    "pd.set_option('mode.chained_assignment', None)\n",
    "pd.set_option('display.float_format', '{:.4f}'.format)\n",
    "np.set_printoptions(suppress=True, precision=4)\n",
    "os.environ['PYTHONHASHSEED'] = '0'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trading Bot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import finance\n",
    "import tradingbot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "symbol = 'EUR='\n",
    "features = [symbol, 'r', 's', 'm', 'v']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 0\n",
    "b = 1750\n",
    "c = 250"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "learn_env = finance.Finance(symbol, features, window=20, lags=3,\n",
    "                 leverage=1, min_performance=0.9, min_accuracy=0.475,\n",
    "                 start=a, end=a + b, mu=None, std=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "learn_env.data.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "valid_env = finance.Finance(symbol, features=learn_env.features,\n",
    "                            window=learn_env.window,\n",
    "                            lags=learn_env.lags,\n",
    "                            leverage=learn_env.leverage,\n",
    "                            min_performance=0.0, min_accuracy=0.0,\n",
    "                            start=a + b, end=a + b + c,\n",
    "                            mu=learn_env.mu, std=learn_env.std)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "valid_env.data.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tradingbot.set_seeds(100)\n",
    "agent = tradingbot.TradingBot(24, 0.001, learn_env, valid_env)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "episodes = 61"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%time agent.learn(episodes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tradingbot.plot_treward(agent)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tradingbot.plot_performance(agent)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Vectorized Backtesting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def reshape(s):\n",
    "    return np.reshape(s, [1, learn_env.lags,\n",
    "                          learn_env.n_features])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def backtest(agent, env):\n",
    "    env.min_accuracy = 0.0\n",
    "    env.min_performance = 0.0\n",
    "    done = False\n",
    "    env.data['p'] = 0\n",
    "    state = env.reset()\n",
    "    while not done:\n",
    "        action = np.argmax(\n",
    "            agent.model.predict(reshape(state))[0, 0])\n",
    "        position = 1 if action == 1 else -1\n",
    "        env.data.loc[:, 'p'].iloc[env.bar] = position\n",
    "        state, reward, done, info = env.step(action)\n",
    "    env.data['s'] = env.data['p'] * env.data['r'] * learn_env.leverage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = agent.learn_env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest(agent, env)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data['p'].iloc[env.lags:].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].sum().apply(np.exp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].sum().apply(np.exp) - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].cumsum(\n",
    "        ).apply(np.exp).plot(figsize=(10, 6));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_env = finance.Finance(symbol, features=learn_env.features,\n",
    "                           window=learn_env.window,\n",
    "                           lags=learn_env.lags,\n",
    "                           leverage=learn_env.leverage,\n",
    "                           min_performance=0.0, min_accuracy=0.0,\n",
    "                           start=a + b + c, end=None,\n",
    "                           mu=learn_env.mu, std=learn_env.std)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = test_env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "backtest(agent, env)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data['p'].iloc[env.lags:].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].sum().apply(np.exp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].sum().apply(np.exp) - 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.data[['r', 's']].iloc[env.lags:].cumsum(\n",
    "            ).apply(np.exp).plot(figsize=(10, 6));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Event-Based Backtesting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import backtesting as bt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb = bt.BacktestingBase(env=agent.learn_env, model=agent.model,\n",
    "                        amount=10000, ptc=0.0001, ftc=1.0,\n",
    "                        verbose=True) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.initial_amount"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bar = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.get_date_price(bar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.env.get_state(bar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.place_buy_order(bar, amount=5000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.print_net_wealth(2 * bar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.place_sell_order(2 * bar, units=1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bb.close_out(3 * bar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TBBacktester(bt.BacktestingBase):\n",
    "    def _reshape(self, state):\n",
    "        ''' Helper method to reshape state objects.\n",
    "        '''\n",
    "        return np.reshape(state, [1, self.env.lags, self.env.n_features])\n",
    "    def backtest_strategy(self):\n",
    "        ''' Event-based backtesting of the trading bot's performance.\n",
    "        '''\n",
    "        self.units = 0\n",
    "        self.position = 0\n",
    "        self.trades = 0\n",
    "        self.current_balance = self.initial_amount\n",
    "        self.net_wealths = list()\n",
    "        for bar in range(self.env.lags, len(self.env.data)):\n",
    "            date, price = self.get_date_price(bar)\n",
    "            if self.trades == 0:\n",
    "                print(50 * '=')\n",
    "                print(f'{date} | *** START BACKTEST ***')\n",
    "                self.print_balance(bar)\n",
    "                print(50 * '=')\n",
    "            state = self.env.get_state(bar)\n",
    "            action = np.argmax(self.model.predict(\n",
    "                        self._reshape(state.values))[0, 0])\n",
    "            position = 1 if action == 1 else -1\n",
    "            if self.position in [0, -1] and position == 1:\n",
    "                if self.verbose:\n",
    "                    print(50 * '-')\n",
    "                    print(f'{date} | *** GOING LONG ***')\n",
    "                if self.position == -1:\n",
    "                    self.place_buy_order(bar - 1, units=-self.units)\n",
    "                self.place_buy_order(bar - 1,\n",
    "                                     amount=self.current_balance)\n",
    "                if self.verbose:\n",
    "                    self.print_net_wealth(bar)\n",
    "                self.position = 1\n",
    "            elif self.position in [0, 1] and position == -1:\n",
    "                if self.verbose:\n",
    "                    print(50 * '-')\n",
    "                    print(f'{date} | *** GOING SHORT ***')\n",
    "                if self.position == 1:\n",
    "                    self.place_sell_order(bar - 1, units=self.units)\n",
    "                self.place_sell_order(bar - 1,\n",
    "                                      amount=self.current_balance)\n",
    "                if self.verbose:\n",
    "                    self.print_net_wealth(bar)\n",
    "                self.position = -1\n",
    "            self.net_wealths.append((date,\n",
    "                                     self.calculate_net_wealth(price)))\n",
    "        self.net_wealths = pd.DataFrame(self.net_wealths,\n",
    "                                        columns=['date', 'net_wealth'])\n",
    "        self.net_wealths.set_index('date', inplace=True)\n",
    "        self.net_wealths.index = pd.DatetimeIndex(\n",
    "                                        self.net_wealths.index)\n",
    "        self.close_out(bar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = learn_env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb = TBBacktester(env, agent.model, 10000,\n",
    "                  0.0, 0, verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb_ = TBBacktester(env, agent.model, 10000,\n",
    "                   0.00012, 0.0, verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb_.backtest_strategy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ax = tb.net_wealths.plot(figsize=(10, 6))\n",
    "tb_.net_wealths.columns = ['net_wealth (after tc)']\n",
    "tb_.net_wealths.plot(ax=ax);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = test_env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb = TBBacktester(env, agent.model, 10000,\n",
    "                  0.0, 0, verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb_ = TBBacktester(env, agent.model, 10000,\n",
    "                   0.00012, 0.0, verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb_.backtest_strategy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ax = tb.net_wealths.plot(figsize=(10, 6))\n",
    "tb_.net_wealths.columns = ['net_wealth (after tc)']\n",
    "tb_.net_wealths.plot(ax=ax);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ax = (tb.net_wealths / tb.net_wealths.iloc[0]).plot(figsize=(10, 6))\n",
    "tp = env.data[['r', 's']].iloc[env.lags:].cumsum().apply(np.exp)\n",
    "(tp / tp.iloc[0]).plot(ax=ax);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Assessing Risk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.DataFrame(learn_env.data[symbol])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "window = 14"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['min'] = data[symbol].rolling(window).min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['max'] = data[symbol].rolling(window).max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['mami'] = data['max'] - data['min']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['mac'] = abs(data['max'] - data[symbol].shift(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['mic'] = abs(data['min'] - data[symbol].shift(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['atr'] = np.maximum(data['mami'], data['mac'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['atr'] = np.maximum(data['atr'], data['mic'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['atr%'] = data['atr'] / data[symbol]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data[['atr', 'atr%']].plot(subplots=True, figsize=(10, 6));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data[['atr', 'atr%']].tail()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "leverage = 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data[['atr', 'atr%']].tail() * leverage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data[['atr', 'atr%']].median() * leverage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Backtesting Risk Measures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tbbacktesterrm as tbbrm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "env = test_env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb = tbbrm.TBBacktesterRM(env, agent.model, 10000,\n",
    "                          0.0, 0, verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=None, tsl=None, tp=None, wait=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Stop Loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=0.0175, tsl=None, tp=None,\n",
    "                     wait=5, guarantee=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=0.017, tsl=None, tp=None,\n",
    "                     wait=5, guarantee=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Trailing Stop Loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=None, tsl=0.015,\n",
    "                     tp=None, wait=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Take Profit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=None, tsl=None, tp=0.015,\n",
    "                     wait=5, guarantee=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=None, tsl=None, tp=0.015,\n",
    "                     wait=5, guarantee=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Combinations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=0.015, tsl=None,\n",
    "                     tp=0.0185, wait=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tb.backtest_strategy(sl=None, tsl=0.02,\n",
    "                     tp=0.02, wait=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src='http://hilpisch.com/taim_logo.png' width=\"350px\" align=\"right\">\n",
    "\n",
    "<br><br><br><a href=\"http://tpq.io\" target=\"_blank\">http://tpq.io</a> | <a href=\"http://twitter.com/dyjh\" target=\"_blank\">@dyjh</a> | <a href=\"mailto:ai@tpq.io\">ai@tpq.io</a>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
